package org.study.minio.boot.controller;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.ModelAndView;
import org.study.minio.boot.config.MinioConstant;

import java.io.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 大文件分片上传：每个分片的数据都存储在内存中(这里用Map保存)，分片上传完毕后再合并，同时删除分片文件
 *
 * @author doveylovey
 * @version v1.0.0
 * @email 1135782208@qq.com
 * @date 2023年12月14日
 */
@Slf4j
@CrossOrigin
@RestController
@RequestMapping("/memory")
public class FileUploadMemoryController {
    // 上传文件的根目录
    private static final String UPLOAD_DIR = MinioConstant.DESKTOP + File.separator + "uploads";

    // 用于存储每个文件的分片信息，使用 ConcurrentHashMap 以支持并发
    private static final ConcurrentHashMap<String, byte[]> chunksMap = new ConcurrentHashMap<>();

    @RequestMapping
    public ModelAndView toChatGpt1() {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("upload1");
        return modelAndView;
    }

    @PostMapping("/chunks")
    public ResponseEntity<String> uploadChunk(@RequestParam("file") MultipartFile file,
                                              @RequestParam("chunkNumber") int chunkNumber,
                                              @RequestParam("totalChunks") int totalChunks,
                                              @RequestParam("identifier") String identifier) throws IOException {
        try {
            // 将分片数据存储到 ConcurrentHashMap 中
            byte[] chunkData = file.getBytes();
            // 将分片数据存入 ConcurrentHashMap，使用文件标识符和分片编号作为 key
            chunksMap.put(identifier + "-" + chunkNumber, chunkData);
        } catch (IOException e) {
            return new ResponseEntity<>("读取分片文件异常：" + e.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
        }
        if (chunkNumber == totalChunks - 1) {
            // 如果是最后一个分片，进行文件合并
            if (this.mergeChunks(identifier, identifier)) {
                // 清除分片数据
                for (int i = 0; i < chunksMap.size(); i++) {
                    chunksMap.remove(identifier + "-" + i);
                }
                return new ResponseEntity<>("合并分片成功", HttpStatus.OK);
            } else {
                return new ResponseEntity<>("合并分片失败", HttpStatus.INTERNAL_SERVER_ERROR);
            }
        }
        return new ResponseEntity<>("上传分片文件上传成功", HttpStatus.OK);
    }

    /**
     * 合并分片文件为完整文件
     *
     * @param identifier 文件标识符，用于识别同一个文件的所有分片
     * @param filename   文件名
     * @return 合并是否成功
     */
    private boolean mergeChunks(String identifier, String filename) throws IOException {
        // 创建上传目录
        File uploadDir = new File(UPLOAD_DIR);
        if (!uploadDir.exists()) {
            uploadDir.mkdirs();
        }
        // 创建最终合并后的文件
        File mergedFile = new File(UPLOAD_DIR, filename);
        try (FileOutputStream fos = new FileOutputStream(mergedFile, true)) {
            // 循环遍历分片文件并逐一合并
            for (int i = 0; i < chunksMap.size(); i++) {
                byte[] chunkData = chunksMap.get(identifier + "-" + i);
                // 将分片文件内容追加到最终合并文件中
                fos.write(chunkData);
            }
            log.info("文件合并后的位置：" + mergedFile.getAbsolutePath());
            return true;
        }
    }
}
